package zlog

import (
	"fmt"
	"github.com/natefinch/lumberjack"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"os"
	"strings"
	"time"
)

// 日志配置节点
type ZapLogNode struct {
	// 等级
	Level string `xml:"level,attr" json:"level" mapstructure:"level"`
	// 是否写文件
	File bool `xml:"file,attr" json:"file" mapstructure:"file"`
	// 文件写入地方,相对路径和名字一起
	FileDest string `xml:"file_dest,attr" json:"file_dest" mapstructure:"file_dest"`
	// 文件分割大小(单位mb)
	FileSize int `xml:"file_size,attr" json:"file_size" mapstructure:"file_size"`
	// 是否写屏幕
	Console bool `xml:"console,attr" json:"console" mapstructure:"console"`
	// 日志是否打印文件名和函数信息(别人使用的时候不太关心可以不要，开发调试时需要)
	LogGPS bool `xml:"log_gps,attr" json:"log_gps" mapstructure:"log_gps"`
}

// 设置zap日志的文件写入属性
func getZapLogWriter(logNode *ZapLogNode) zapcore.WriteSyncer {
	if len(logNode.FileDest) < 1 ||
		logNode.FileSize < 1 {
		return nil
	}
	jackLogger := &lumberjack.Logger{
		Filename:   logNode.FileDest, // 打印的日志文件名
		MaxSize:    logNode.FileSize, // 切割大小，单位MB
		MaxAge:     30,               // 保存天数
		MaxBackups: 0,                // 文件保存个数,和上面个二选一
		LocalTime:  true,             // 是否使用本地时间,false是UTC时间
		Compress:   false,            // 是否压缩文件
	}
	return zapcore.AddSync(jackLogger)
}

// @Desc: 日志格式
// @param needGPS 是否需要定位(打钱文件,函数,行号）
// @return zapcore.Encoder
func getZapLogEncoder(needGPS bool) zapcore.Encoder {
	// 时间自定义格式
	timeEncoder := func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
		enc.AppendString(t.Format("2006-01-02 15:04:05.000"))
	}
	cfg := zap.NewProductionEncoderConfig()
	cfg.EncodeTime = timeEncoder                  // 时间的显示格式
	cfg.EncodeLevel = zapcore.CapitalLevelEncoder // 日志等级的显示格式(颜色)
	cfg.ConsoleSeparator = " "                    //字段的间隔方式，默认是\t, 这里改成了空格
	if needGPS {
		// 调用堆栈自定义,这里是普通版才设置
		cfg.EncodeCaller = func(caller zapcore.EntryCaller, enc zapcore.PrimitiveArrayEncoder) {
			if absPath := strings.Split(caller.File, "/"); len(absPath) < 1 {
				enc.AppendString(fmt.Sprintf("%s(%s:%d)", caller.Function, caller.File, caller.Line))
			} else {
				enc.AppendString(fmt.Sprintf("%s(%s:%d)", caller.Function, absPath[len(absPath)-1], caller.Line))
			}
		}
	} else {
		cfg.EncodeCaller = nil
	}
	return zapcore.NewConsoleEncoder(cfg)
}

func getZapLogLevel(logLevel *string) zapcore.Level {
	switch strings.ToLower(*logLevel) {
	case "debug":
		return zapcore.DebugLevel
	case "info":
		return zapcore.InfoLevel
	case "warn":
		return zapcore.WarnLevel
	case "error":
		return zapcore.ErrorLevel
	case "fatal":
		return zapcore.FatalLevel
	default:
		return zapcore.DebugLevel
	}
	return zapcore.DebugLevel
}

// 初始化日志实例
func InitLogger(node *ZapLogNode) bool {
	fmt.Println("[日志] 开始初始化日志模块")
	encoder := getZapLogEncoder(node.LogGPS) // 日志格式
	var writerList []zapcore.WriteSyncer
	if node.File {
		writerList = append(writerList, getZapLogWriter(node))
	}
	if node.Console {
		writerList = append(writerList, os.Stdout)
	}
	if len(writerList) < 1 {
		fmt.Println("[日志] 文件和屏幕都不写,所以未做任何事情")
		return false
	}
	writer := zapcore.NewMultiWriteSyncer(writerList...)
	logger := zap.New(zapcore.NewCore(encoder,
		writer,
		getZapLogLevel(&node.Level)), //日志等级
		zap.AddCaller()) //有了这行,才会打印文件和行数
	zap.ReplaceGlobals(logger)
	zap.S().Info("[日志] 初始化成功")
	return true
}
